package controller

import (
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"gin/common"
	"gin/queueAmqp"
	"gin/response"
	"gin/service"
	"gin/tai/sentence"
	"gin/tai/voice"
	"gin/validate"
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"github.com/spf13/viper"
	"gorm.io/gorm"
	"io/ioutil"
	"os"
	"time"
)

func EventSelect(ctx *gin.Context) {
	var selectDictQuery validate.SelectEventQuery

	err := ctx.ShouldBindJSON(&selectDictQuery)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	selectQueryParam := common.GenerateSelectQueryParam(selectDictQuery.Param, "log")

	total, resData, queryErr := service.SelectEvent(&selectQueryParam)

	if queryErr != nil {
		// 查询失败
		response.Fail(ctx, nil, queryErr.Error())
	} else {
		// 查询成功
		response.Success(ctx, gin.H{"data": gin.H{"total": total, "rows": resData}}, "success")
	}
}

// 查询单条词库
func EventFind(ctx *gin.Context) {
	var selectDictQuery validate.FindEventQuery

	err := ctx.ShouldBindJSON(&selectDictQuery)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	findQueryParam := common.GenerateFilterParam(&selectDictQuery.Param, "log")

	resData, queryErr := service.FindEvent(findQueryParam)

	if queryErr != nil {
		// 查询失败
		response.Fail(ctx, nil, queryErr.Error())
	} else {
		// 查询成功
		response.Success(ctx, gin.H{"data": resData}, "success")
	}
}

// 插入日志
func EventCreate(ctx *gin.Context) {
	var addEvent validate.AddEventWrapper

	err := ctx.ShouldBindJSON(&addEvent)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	resData, createErr := service.CreateEvent(addEvent.Data)
	if createErr != nil {
		// 插入失败
		response.Fail(ctx, nil, createErr.Error())
		return
	}

	response.Success(ctx, gin.H{"data": resData}, "success")
}

func EventUpdate(ctx *gin.Context) {
	var updateEvent validate.UpdateEventWrapper

	err := ctx.ShouldBindBodyWith(&updateEvent, binding.JSON)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	var updateFields validate.EventUpdateFields

	err = ctx.ShouldBindBodyWith(&updateFields, binding.JSON)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	_, ok := updateFields.Data["id"]
	if ok {
		delete(updateFields.Data, "id")
	}
	resData, err := service.UpdateEvent(updateEvent.Data.Id, updateFields.Data)
	if err != nil {
		//插入失败
		response.Fail(ctx, nil, err.Error())
		return
	}

	response.Success(ctx, gin.H{"data": resData}, "success")
}

func EventDelete(ctx *gin.Context) {
	var deleteEventParam validate.DeleteEventParam

	err := ctx.ShouldBindJSON(&deleteEventParam)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	selectQueryParam := common.GenerateFilterParam(&deleteEventParam.Param, "sentence")

	ef, err := service.DeleteEvent(&selectQueryParam)
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	// 查询成功
	response.Success(ctx, gin.H{"data": ef}, "success")

}

// 事件分析台词
func EventAnalyzingLines(ctx *gin.Context) {
	var v validate.EventAnalyzingLines

	err := ctx.ShouldBind(&v)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	mediaId := uint64(0)
	videoMediaId := uint64(0)

	taskQueryList := make([]uint64, 0)

	event, err := service.FindEvent(common.Filter{Filter: map[string]interface{}{
		"-eq": map[string]interface{}{
			"condition": "id = ?",
			"val":       v.EventId,
		},
	}})
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	role := event.PlayRole.Role

	if v.Type == "text" {
		// 1-云小奇，亲和男声
		var voiceType int64
		if role.Gender == "male" {
			voiceType = 1
		} else {
			//4-云小叶，温暖女声
			voiceType = 4
		}
		fileExt := "wav"
		res, err := sentence.TextToVoice(v.Text, voiceType, fileExt)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		fileByte, err := base64.StdEncoding.DecodeString(*res.Response.Audio)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		md5Str := common.GenerateMd5ByBytes(fileByte)

		media, err := service.FindMediaByMd5(md5Str)
		if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
			response.Fail(ctx, nil, err.Error())
			return
		}
		// 当查询发生错误 或者media不存在的时候 就新建媒体
		if (err != nil && errors.Is(err, gorm.ErrRecordNotFound)) || media.Id <= 0 {
			// 文件名称
			folderName := common.RandStringBytesMaskImprSrcUnsafe(10) + "_" + common.GenerateMd5(time.Now().String())

			folderPath := "static/" + folderName
			err = common.CreateDirectory("./" + folderPath)
			if err != nil {
				response.Fail(ctx, nil, err.Error())
				return
			}
			dstName := folderName + "." + fileExt
			tempPath := viper.GetString("server.staticPath") + "temp/" + dstName
			err = ioutil.WriteFile(tempPath, fileByte, 0644)

			if err != nil {
				response.Fail(ctx, nil, err.Error())
				return
			}

			metaData := validate.MediaMetadata{}
			metaData.Md5 = common.GetFileMd5(tempPath)
			metaData.Md5Name = folderName
			metaData.Ext = fileExt
			metaData.FileSize = int64(len(fileByte))

			// 文件夹名称
			metaData.Folder = folderName
			metaData.Path = folderPath + "/"
			metaData.Type = "audio"
			metaData.NamePrefix = folderName
			_, err = ConvertAudio(metaData, tempPath)
			if err != nil {
				response.Fail(ctx, nil, err.Error())
				return
			}
			addMedia := validate.AddMedia{}
			addMedia.Md5Name = folderName
			addMedia.Md5 = metaData.Md5
			addMedia.Type = metaData.Type
			addMedia.Status = "yes"

			metaData.RelativePath = fmt.Sprintf("/static/%s/%s.%s", metaData.Md5Name, metaData.Md5Name, metaData.Ext)
			addMedia.RelativePath = metaData.RelativePath

			addMedia.Param, _ = json.Marshal(metaData)
			mediaId, err = service.CreateMedia(addMedia)
			if err != nil {
				response.Fail(ctx, nil, err.Error())
				return
			}
		} else {
			mediaId = media.Id
		}

		if media.TaskResultFile == "" {
			// 协程处理创建媒体分析任务
			go service.CreateMediaAnalysisTask(mediaId)
			taskQueryList = append(taskQueryList, mediaId)
		}

	} else if v.Type == "audio" {
		_, fh, err := ctx.Request.FormFile("file")
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		metaData, err := DealMediaItem(ctx, fh, "")
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
		mediaId = metaData.MediaId
		go service.CreateMediaAnalysisTask(mediaId)
		taskQueryList = append(taskQueryList, mediaId)

		media, _ := service.FindMedia(common.Filter{
			Filter: map[string]interface{}{
				"-eq": map[string]interface{}{
					"condition": "id = ?",
					"val":       mediaId,
				}}})

		workDir, _ := os.Getwd()
		fcBytes, err := ioutil.ReadFile(workDir + media.RelativePath)
		if err != nil {
			response.Fail(ctx, nil, "[media] "+err.Error())
			return
		}

		fContentStr := base64.StdEncoding.EncodeToString(fcBytes)

		resp, err := voice.SentenceRecognition(0, 1, media.Uuid, metaData.Ext, "", fContentStr, int64(len(fcBytes)))
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		v.Text = *resp.Response.Result
	} else if v.Type == "video" {

		_, fh, err := ctx.Request.FormFile("file")
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		metaData, err := DealMediaItem(ctx, fh, "")
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		videoMediaId = metaData.MediaId
		go service.CreateMediaAnalysisTask(videoMediaId)
		taskQueryList = append(taskQueryList, videoMediaId)

		fileExt := viper.GetString("ffmpeg.default_audio_format")
		ve := validate.ConvertAudioFormat{
			TempPath:   "." + metaData.RelativePath,
			Path:       metaData.Path,
			Md5Name:    metaData.Md5Name,
			FileFormat: fileExt,
		}

		relativePath, err := queueAmqp.HandleExtractAudioDefaultFormat(ve)

		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
		tempPath := relativePath
		fcBytes, err := ioutil.ReadFile(relativePath)

		metaDataAudio := validate.MediaMetadata{}
		metaDataAudio.Md5 = common.GetFileMd5(tempPath)
		metaDataAudio.Ext = fileExt
		metaDataAudio.FileSize = int64(len(fcBytes))

		// 文件名称
		folderName := common.RandStringBytesMaskImprSrcUnsafe(10) + "_" + common.GenerateMd5(time.Now().String())

		folderPath := "static/" + folderName
		err = common.CreateDirectory("./" + folderPath)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		metaDataAudio.Md5Name = folderName
		// 文件夹名称
		metaDataAudio.Folder = folderName
		metaDataAudio.Path = folderPath + "/"
		metaDataAudio.Type = "audio"
		metaDataAudio.NamePrefix = folderName
		metaDataAudio.RelativePath = fmt.Sprintf("/static/%s/%s.%s", metaDataAudio.Md5Name, metaDataAudio.Md5Name, metaDataAudio.Ext)

		_, err = ConvertAudio(metaDataAudio, tempPath)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
		addMedia := validate.AddMedia{}
		addMedia.Md5Name = folderName
		addMedia.Md5 = metaDataAudio.Md5
		addMedia.Type = metaDataAudio.Type
		addMedia.Status = "yes"

		metaData.RelativePath = fmt.Sprintf("/static/%s/%s.%s", metaData.Md5Name, metaData.Md5Name, metaData.Ext)
		addMedia.RelativePath = metaDataAudio.RelativePath
		addMedia.Param, _ = json.Marshal(metaDataAudio)
		mediaId, err = service.CreateMedia(addMedia)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
		go service.CreateMediaAnalysisTask(mediaId)
		taskQueryList = append(taskQueryList, mediaId)

		media, err := service.FindMedia(common.Filter{Filter: map[string]interface{}{
			"-eq": map[string]interface{}{
				"condition": "id = ?",
				"val":       mediaId,
			},
		}},
		)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}

		if media.Type != "audio" {
			response.Fail(ctx, nil, "[media] type is not audio")
			return
		}

		fileExt = metaDataAudio.Ext
		resp, err := voice.SentenceRecognition(0, 1, media.Uuid, fileExt, "", base64.StdEncoding.EncodeToString(fcBytes), metaDataAudio.FileSize)
		if err != nil {
			response.Fail(ctx, nil, err.Error())
			return
		}
		v.Text = *resp.Response.Result
	}

	res, err := service.UpdateEvent(v.EventId, map[string]interface{}{
		"line_text":           v.Text,
		"line_media_id":       mediaId,
		"line_video_media_id": videoMediaId,
	})
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 分析面部

	service.QueryTaskStatus(taskQueryList, viper.GetInt64("cdai.query_task_max_time"))

	// 重新查询任务
	res, err = service.FindEvent(common.Filter{Filter: map[string]interface{}{
		"-eq": map[string]interface{}{
			"condition": "id = ?",
			"val":       v.EventId,
		}},
	})
	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	// 查询成功
	response.Success(ctx, gin.H{"data": res}, "success")
}

// 事件批量更新
func EventUpdateByFilter(ctx *gin.Context) {
	var updateEventParam validate.UpdateByFilter

	err := ctx.ShouldBindJSON(&updateEventParam)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}

	selectQueryParam := common.GenerateFilterParam(&updateEventParam.Param, "event")

	rowAffect, err := service.UpdateEventByFilter(&selectQueryParam, updateEventParam.Data)

	if err != nil {
		response.Fail(ctx, nil, err.Error())
		return
	}
	// 查询成功
	response.Success(ctx, gin.H{"data": rowAffect}, "success")
}
